home *** CD-ROM | disk | FTP | other *** search
/ MacGames Sampler / PHT MacGames Bundle.iso / MacSource Folder / Samples from the CD / C and C++ / Gnuplot 3.5 for Macintosh / SOURCES 3.5 / help.c < prev    next >
Text File  |  1993-11-12  |  20KB  |  748 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: help.c%v 3.50.1.16 1993/08/27 05:04:42 woo Exp $";
  3. #endif
  4.  
  5.  
  6. /* GNUPLOT - help.c */
  7. /*
  8.  * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted, 
  12.  * provided that the above copyright notice appear in all copies and 
  13.  * that both that copyright notice and this permission notice appear 
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the modified code.  Modifications are to be distributed 
  18.  * as patches to released version.
  19.  *  
  20.  * This software is provided "as is" without express or implied warranty.
  21.  * 
  22.  *
  23.  * AUTHORS
  24.  * 
  25.  *   Original Software:
  26.  *     Thomas Williams,  Colin Kelley.
  27.  * 
  28.  *   Gnuplot 2.0 additions:
  29.  *       Russell Lang, Dave Kotz, John Campbell.
  30.  *
  31.  *   Gnuplot 3.0 additions:
  32.  *       Gershon Elber and many others.
  33.  * 
  34.  * There is a mailing list for gnuplot users. Note, however, that the
  35.  * newsgroup 
  36.  *     comp.graphics.gnuplot 
  37.  * is identical to the mailing list (they
  38.  * both carry the same set of messages). We prefer that you read the
  39.  * messages through that newsgroup, to subscribing to the mailing list.
  40.  * (If you can read that newsgroup, and are already on the mailing list,
  41.  * please send a message info-gnuplot-request@dartmouth.edu, asking to be
  42.  * removed from the mailing list.)
  43.  *
  44.  * The address for mailing to list members is
  45.  *       info-gnuplot@dartmouth.edu
  46.  * and for mailing administrative requests is 
  47.  *       info-gnuplot-request@dartmouth.edu
  48.  * The mailing list for bug reports is 
  49.  *       bug-gnuplot@dartmouth.edu
  50.  * The list of those interested in beta-test versions is
  51.  *       info-gnuplot-beta@dartmouth.edu
  52.  */
  53.  
  54. #include <stdio.h>
  55. #if defined (ATARI)
  56. #include "plot.h"
  57. #else
  58.  
  59. extern int strcmp();
  60.  
  61. #ifdef THINK_C
  62. #include "plot.h"
  63. #include "tout_protos.h"
  64. extern size_t strlen(const char *s);
  65. #else
  66. extern int  strlen();
  67. #endif
  68.  
  69. extern char *strcpy();
  70. extern char *strncpy();
  71. extern char *strcat();
  72. extern char *strncat();
  73. extern char *getenv();
  74. extern FILE *fopen();
  75. #ifdef THINK_C
  76. extern void *malloc(size_t);
  77. #else
  78. extern char *malloc();
  79. #endif
  80.  
  81. #endif
  82.  
  83. #include <errno.h>
  84. extern int errno;
  85.  
  86. #ifdef THINK_C
  87. extern int instring(char *, char);
  88. #else
  89. extern int instring();
  90. #endif
  91.  
  92. #define    SAME    0    /* for strcmp() */
  93.  
  94. #include "help.h"    /* values passed back */
  95.  
  96. #if defined(__EMX__) || defined(DJGPP) || defined(DOS386)
  97. #ifdef MSDOS
  98. #undef MSDOS    /* we have plenty of memory under __EMX__ or DJGPP */
  99. #endif
  100. #ifdef unix
  101. #undef unix    /* we are not unix */
  102. #endif
  103. #endif
  104.  
  105. #ifdef OS2
  106.   /* GCC defines unix, but no PAGER, so... */
  107. #ifdef unix
  108. #undef unix
  109. #endif
  110. #endif  /* OS2 */
  111.  
  112. /* help -- help subsystem that understands defined keywords
  113. **
  114. ** Looks for the desired keyword in the help file at runtime, so you
  115. ** can give extra help or supply local customizations by merely editing
  116. ** the help file.
  117. **
  118. ** The original (single-file) idea and algorithm is by John D. Johnson,
  119. ** Hewlett-Packard Company.  Thanx and a tip of the Hatlo hat!
  120. **
  121. ** Much extension by David Kotz for use in gnutex, and then in gnuplot.
  122. ** Added output paging support, both unix and builtin. Rewrote completely
  123. ** to read helpfile into memory, avoiding reread of help file. 12/89.
  124. **
  125. ** Modified by Russell Lang to avoid reading completely into memory
  126. ** if MSDOS defined.  This uses much less memory.  6/91
  127. **
  128. ** The help file looks like this (the question marks are really in column 1):
  129. **
  130. **     ?topic
  131. **     This line is printed when the user wants help on "topic".
  132. **     ?keyword
  133. **     ?Keyword
  134. **     ?KEYWORD
  135. **     These lines will be printed on the screen if the user wanted
  136. **     help on "keyword", "Keyword", or "KEYWORD".  No casefolding is
  137. **    done on the keywords.
  138. **     ?subject
  139. **     ?alias
  140. **     This line is printed for help on "subject" and "alias".
  141. **     ?
  142. **    ??
  143. **     Since there is a null keyword for this line, this section
  144. **     is printed when the user wants general help (when a help
  145. **     keyword isn't given).  A command summary is usually here.
  146. **    Notice that the null keyword is equivalent to a "?" keyword
  147. **    here, because of the '?' and '??' topic lines above.
  148. **   If multiple keywords are given, the first is considered the 
  149. **   'primary' keyword. This affects a listing of available topics.
  150. **     ?last-subject
  151. **     Note that help sections are terminated by the start of the next
  152. **     '?' entry or by EOF.  So you can't have a leading '?' on a line
  153. **     of any help section.  You can re-define the magic character to
  154. **    recognize in column 1, though, if '?' is too useful.  (Try ^A.)
  155. */
  156.  
  157. #define    KEYFLAG    '?'    /* leading char in help file topic lines */
  158.  
  159. /*
  160. ** Calling sequence:
  161. **    int result;        # 0 == success
  162. **    char *keyword;        # topic to give help on
  163. **    char *pathname;        # path of help file
  164. **      int subtopics;        # set to TRUE if only subtopics to be listed
  165. **                # returns TRUE if subtopics were found
  166. **    result = help(keyword, pathname, &subtopics);
  167. ** Sample:
  168. **    cmd = "search\n";
  169. **    helpfile = "/usr/local/lib/program/program.help";
  170. **    subtopics = FALSE;
  171. **    if (help(cmd, helpfile, &subtopics) != H_FOUND)
  172. **        printf("Sorry, no help for %s", cmd);
  173. **
  174. **
  175. ** Speed this up by replacing the stdio calls with open/close/read/write.
  176. */
  177. #ifdef    WDLEN
  178. #  define    PATHSIZE    WDLEN
  179. #else
  180. #  define    PATHSIZE    BUFSIZ
  181. #endif
  182.  
  183. typedef int boolean;
  184. #ifndef TRUE
  185. #define TRUE (1)
  186. #define FALSE (0)
  187. #endif
  188.  
  189. typedef struct line_s LINEBUF;
  190. struct line_s {
  191.     char *line;            /* the text of this line */
  192.     LINEBUF *next;            /* the next line */
  193. };
  194.  
  195. typedef struct linkey_s LINKEY;
  196. struct linkey_s {
  197.     char *key;                /* the name of this key */
  198.     long pos;                /* ftell position */
  199.     LINEBUF *text;            /* the text for this key */
  200.     boolean primary;        /* TRUE -> is a primary name for a text block */
  201.     LINKEY *next;            /* the next key in linked list */
  202. };
  203.  
  204. typedef struct key_s KEY;
  205. struct key_s {
  206.     char *key;                /* the name of this key */
  207.     long pos;                /* ftell position */
  208.     LINEBUF *text;            /* the text for this key */
  209.     boolean primary;        /* TRUE -> is a primary name for a text block */
  210. };
  211. static LINKEY *keylist = NULL;    /* linked list of keys */
  212. static KEY *keys = NULL;        /* array of keys */
  213. static int keycount = 0;        /* number of keys */
  214. static FILE *helpfp = NULL;
  215.  
  216. static int LoadHelp();
  217. static void sortkeys();
  218. static int keycomp();
  219. static LINEBUF *storeline();
  220. static LINKEY *storekey();
  221. static KEY *FindHelp();
  222. static boolean Ambiguous();
  223.  
  224. /* Help output */
  225. static void PrintHelp();
  226. static void ShowSubtopics();
  227. static void StartOutput();
  228. static void OutLine();
  229. static void EndOutput();
  230. static FILE *outfile;        /* for unix pager, if any */
  231. static int pagelines;        /* count for builtin pager */
  232. #define SCREENSIZE 24        /* lines on screen (most have at least 24) */
  233.  
  234. /* help:
  235.  * print a help message 
  236.  * also print available subtopics, if subtopics is TRUE
  237.  */
  238. help(keyword, path, subtopics)
  239.     char *keyword;        /* on this topic */
  240.     char *path;            /* from this file */
  241.     boolean *subtopics;    /* (in) - subtopics only? */
  242.                         /* (out) - are there subtopics? */
  243. {
  244.     static char oldpath[PATHSIZE] = "";    /* previous help file */
  245.     int status;            /* result of LoadHelp */
  246.     KEY *key;            /* key that matches keyword */
  247.  
  248.     /*
  249.     ** Load the help file if necessary (say, first time we enter this routine,
  250.     ** or if the help file changes from the last time we were called).
  251.     ** Also may occur if in-memory copy was freed.
  252.     ** Calling routine may access errno to determine cause of H_ERROR.
  253.     */
  254.     errno = 0;
  255.     if (strncmp(oldpath, path, PATHSIZE) != SAME)
  256.      FreeHelp();
  257.     if (keys == NULL) {
  258.        status = LoadHelp(path);
  259.        if (status == H_ERROR)
  260.         return(status);
  261.  
  262.        /* save the new path in oldpath */
  263.        if (strlen(path) < PATHSIZE)
  264.         (void) strcpy(oldpath, path);
  265.        else {                /* not enough room in oldpath, sigh */
  266.           (void) strncpy(oldpath, path, PATHSIZE - 1);
  267.           oldpath[PATHSIZE - 1] = '\0';
  268.        }
  269.     }
  270.  
  271.     /* look for the keyword in the help file */
  272.     key = FindHelp(keyword);
  273.     if (key != NULL) {
  274.        /* found the keyword: print help and return */
  275.        PrintHelp(key, subtopics);
  276.        status = H_FOUND;
  277.     } else {
  278.        status = H_NOTFOUND;
  279.     }
  280.  
  281.     return(status);
  282. }
  283.  
  284. /* we only read the file once, into memory
  285.  * except for MSDOS when we don't read all the file -
  286.  * just the keys and location of the text
  287.  */
  288. static int
  289. LoadHelp(path)
  290.     char *path;
  291. {
  292.     LINKEY *key;            /* this key */
  293.     long pos;                /* ftell location within help file */
  294.     char buf[BUFSIZ];        /* line from help file */
  295.     LINEBUF *head;            /* head of text list  */
  296.     LINEBUF *firsthead = NULL;
  297.     boolean primary;        /* first ? line of a set is primary */
  298.     boolean flag;
  299.  
  300.     if ((helpfp = fopen(path, "r")) == NULL) {
  301.        /* can't open help file, so error exit */
  302.        return (H_ERROR);
  303.     }
  304.  
  305.     /*
  306.     ** The help file is open.  Look in there for the keyword.
  307.     */
  308.     (void) fgets(buf, BUFSIZ - 1, helpfp);
  309.     while (!feof(helpfp)) {
  310.        /*
  311.         ** Make an entry for each synonym keyword
  312.         */
  313.        primary = TRUE;
  314.        while (buf[0] == KEYFLAG) {
  315.           key = storekey(buf+1);    /* store this key */
  316.           key->primary = primary;
  317.           key->text = NULL;            /* fill in with real value later */
  318.           key->pos = 0;                /* fill in with real value later */
  319.           primary = FALSE;
  320.           pos = ftell(helpfp);
  321.           if (fgets(buf, BUFSIZ - 1, helpfp) == (char *)NULL)
  322.             break;
  323.        }
  324.        /*
  325.         ** Now store the text for this entry.
  326.         ** buf already contains the first line of text.
  327.         */
  328. #ifndef MSDOS
  329.        firsthead = storeline(buf);
  330.        head = firsthead;
  331. #endif
  332.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  333.         && (buf[0] != KEYFLAG) ){
  334. #ifndef MSDOS
  335.           /* save text line */
  336.           head->next = storeline(buf);
  337.           head = head->next;
  338. #endif
  339.        }
  340.        /* make each synonym key point to the same text */
  341.        do {
  342.           key->pos = pos;
  343.           key->text = firsthead;
  344.           flag = key->primary;
  345.           key = key->next;
  346.        } while ( flag!=TRUE  &&  key!=NULL );
  347.     }
  348. #ifndef MSDOS
  349.     (void) fclose(helpfp);
  350. #endif
  351.  
  352.     /* we sort the keys so we can use binary search later */
  353.     sortkeys();
  354.     return(H_FOUND); /* ok */
  355. }
  356.  
  357. /* make a new line buffer and save this string there */
  358. static LINEBUF *
  359. storeline(text)
  360.     char *text;
  361. {
  362.     LINEBUF *new;
  363.  
  364.     new = (LINEBUF *)malloc(sizeof(LINEBUF));
  365.     if (new == NULL)
  366.      int_error("not enough memory to store help file", -1);
  367.     if (text != NULL) {
  368.        new->line = (char *) malloc((unsigned int)(strlen(text)+1));
  369.        if (new->line == NULL)
  370.         int_error("not enough memory to store help file", -1);
  371.        (void) strcpy(new->line, text);
  372.     } else
  373.      new->line = NULL;
  374.  
  375.     new->next = NULL;
  376.  
  377.     return(new);
  378. }
  379.  
  380. /* Add this keyword to the keys list, with the given text */
  381. static LINKEY *
  382. storekey(key)
  383.     char *key;
  384. {
  385.     LINKEY *new;
  386.  
  387.     key[strlen(key)-1] = '\0'; /* cut off \n  */
  388.  
  389.     new = (LINKEY *)malloc(sizeof(LINKEY));
  390.     if (new == NULL)
  391.      int_error("not enough memory to store help file", -1);
  392.     new->key = (char *) malloc((unsigned int)(strlen(key)+1));
  393.     if (new->key == NULL)
  394.      int_error("not enough memory to store help file", -1);
  395.     (void) strcpy(new->key, key);
  396.  
  397.     /* add to front of list */
  398.     new->next = keylist;
  399.     keylist = new;
  400.     keycount++;
  401.     return(new);
  402. }
  403.  
  404. /* we sort the keys so we can use binary search later */
  405. /* We have a linked list of keys and the number.
  406.  * to sort them we need an array, so we reform them into an array,
  407.  * and then throw away the list.
  408.  */
  409. static void
  410. sortkeys()
  411. {
  412.     LINKEY *p,*n;            /* pointers to linked list */
  413.     int i;                /* index into key array */
  414.     
  415.     /* allocate the array */
  416.     keys = (KEY *)malloc((unsigned int)((keycount+1) * sizeof(KEY)));
  417.     if (keys == NULL)
  418.      int_error("not enough memory to store help file", -1);
  419.     
  420.     /* copy info from list to array, freeing list */
  421.     for (p = keylist, i = 0; p != NULL; p = n, i++) {
  422.        keys[i].key = p->key;
  423.        keys[i].pos = p->pos;
  424.        keys[i].text = p->text;
  425.        keys[i].primary = p->primary;
  426.        n = p->next;
  427.        free( (char *)p );
  428.     }
  429.  
  430.     /* a null entry to terminate subtopic searches */
  431.     keys[keycount].key = NULL;
  432.     keys[keycount].pos = 0;
  433.     keys[keycount].text = NULL;
  434.  
  435.     /* sort the array */
  436.     /* note that it only moves objects of size (two pointers + long + int) */
  437.     /* it moves no strings */
  438.     qsort((char *)keys, keycount, sizeof(KEY), keycomp);
  439. }
  440.  
  441. static int
  442. keycomp(a, b)
  443.     KEY *a,*b;
  444. {
  445.     return (strcmp(a->key, b->key));
  446. }
  447.  
  448. /* Free the help file from memory. */
  449. /* May be called externally if space is needed */
  450. void
  451. FreeHelp()
  452. {
  453.     int i;                /* index into keys[] */
  454.     LINEBUF *t, *next;
  455.  
  456.     if (keys == NULL)
  457.      return;
  458.  
  459.     for (i = 0; i < keycount; i++) {
  460.        free( (char *)keys[i].key );
  461.        if (keys[i].primary)   /* only try to release text once! */
  462.        for (t = keys[i].text; t != NULL; t = next) {
  463.           free( (char *)t->line );
  464.           next = t->next;
  465.           free( (char *)t );
  466.        }
  467.     }
  468.     free( (char *)keys );
  469.     keys = NULL;
  470.     keycount = 0;
  471. #ifdef MSDOS
  472.     (void) fclose(helpfp);
  473. #endif
  474. }
  475.  
  476. /* FindHelp:
  477.  *  Find the key that matches the keyword.
  478.  *  The keys[] array is sorted by key.
  479.  *  We could use a binary search, but a linear search will aid our
  480.  *  attempt to allow abbreviations. We search for the first thing that
  481.  *  matches all the text we're given. If not an exact match, then
  482.  *  it is an abbreviated match, and there must be no other abbreviated
  483.  *  matches -- for if there are, the abbreviation is ambiguous. 
  484.  *  We print the ambiguous matches in that case, and return not found.
  485.  */
  486. static KEY *                /* NULL if not found */
  487. FindHelp(keyword)
  488.     char *keyword;            /* string we look for */
  489. {
  490.     KEY *key;
  491.     int len = strlen(keyword);
  492.     int compare;
  493.  
  494.     for (key = keys, compare = 1; key->key != NULL && compare > 0; key++) {
  495.        compare = strncmp(keyword, key->key, len);
  496.        if (compare == 0)    /* we have a match! */
  497.         if (!Ambiguous(key, len)) {
  498.             /* non-ambiguous abbreviation */
  499.             (void) strcpy(keyword, key->key); /* give back the full spelling */
  500.             return(key);        /* found!! */
  501.         }
  502.     }
  503.  
  504.     /* not found, or ambiguous */
  505.     return(NULL);
  506. }
  507.  
  508. /* Ambiguous:
  509.  * Check the key for ambiguity up to the given length.
  510.  * It is ambiguous if it is not a complete string and there are other
  511.  * keys following it with the same leading substring.
  512.  */
  513. static boolean
  514. Ambiguous(key, len)
  515.     KEY *key;
  516.     int len;
  517. {
  518.     char *first;
  519.     char *prev;
  520.     boolean status = FALSE;    /* assume not ambiguous */
  521.     int compare;
  522.     int sublen;
  523.  
  524.     if (key->key[len] == '\0')
  525.      return(FALSE);
  526.     
  527.     for (prev = first = key->key, compare = 0, key++;
  528.         key->key != NULL && compare == 0; key++) {
  529.        compare = strncmp(first, key->key, len);
  530.        if (compare == 0) {
  531.           /* So this key matches the first one, up to len.
  532.            * But is it different enough from the previous one
  533.            * to bother printing it as a separate choice?
  534.            */
  535.           sublen = instring(prev+len, ' ');
  536.           if (strncmp(key->key, prev, len+sublen) != 0) {
  537.              /* yup, this is different up to the next space */
  538.              if (!status) {
  539.                 /* first one we have printed is special */
  540.                 fprintf(stderr, 
  541.                        "Ambiguous request '%.*s'; possible matches:\n",
  542.                        len, first);
  543.                 fprintf(stderr, "\t%s\n", prev);
  544.                 status = TRUE;
  545.              }
  546.              fprintf(stderr, "\t%s\n", key->key);
  547.              prev = key->key;
  548.           }
  549.        }
  550.     }
  551.     
  552.     return(status);
  553. }
  554.  
  555. /* PrintHelp:
  556.  * print the text for key
  557.  */
  558. static void
  559. PrintHelp(key, subtopics)
  560.     KEY *key;
  561.     boolean *subtopics;        /* (in) - subtopics only? */
  562.                         /* (out) - are there subtopics? */
  563. {
  564.     LINEBUF *t;
  565. #ifdef MSDOS
  566.     char buf[BUFSIZ];        /* line from help file */
  567. #endif
  568.  
  569.     StartOutput();
  570.  
  571.     if (subtopics == NULL || !*subtopics) {
  572. #ifdef MSDOS
  573.        fseek(helpfp,key->pos,0);
  574.        while ( (fgets(buf, BUFSIZ - 1, helpfp) != (char *)NULL)
  575.             && (buf[0] != KEYFLAG) ) {
  576.           OutLine(buf);
  577.        }
  578. #else
  579.        for (t = key->text; t != NULL; t = t->next)
  580.         OutLine(t->line);        /* print text line */
  581. #endif
  582.     }
  583.  
  584.     ShowSubtopics(key, subtopics);
  585.     OutLine("\n");
  586.  
  587.     EndOutput();
  588. }
  589.  
  590. /* ShowSubtopics:
  591.  *  Print a list of subtopic names
  592.  */
  593. #define PER_LINE 4
  594.  
  595. static void
  596. ShowSubtopics(key, subtopics)
  597.     KEY *key;                /* the topic */
  598.     boolean *subtopics;        /* (out) are there any subtopics */
  599. {
  600.     int subt = 0;            /* printed any subtopics yet? */
  601.     KEY *subkey;            /* subtopic key */
  602.     int len;                /* length of key name */
  603.     char line[BUFSIZ];        /* subtopic output line */
  604.     char *start;            /* position of subname in key name */
  605.     int sublen;            /* length of subname */
  606.     int pos;
  607.     char *prev = NULL;        /* the last thing we put on the list */
  608.  
  609.     *line = '\0';
  610.     len = strlen(key->key);
  611.  
  612.     for (subkey = key+1; subkey->key != NULL; subkey++) {
  613.        if (strncmp(subkey->key, key->key, len) == 0) {
  614.           /* find this subtopic name */
  615.           start = subkey->key + len;
  616.           if (len > 0)
  617.             if (*start == ' ')
  618.              start++;        /* skip space */
  619.             else
  620.              break;        /* not the same topic after all  */
  621.           else            /* here we are looking for main topics */
  622.             if (!subkey->primary)
  623.              continue;    /* not a main topic */
  624.           sublen = instring(start, ' ');
  625.           if (prev == NULL || strncmp(start, prev, sublen) != 0) {
  626.              if (subt == 0) {
  627.                 subt++;
  628.                 if (len)
  629.                   (void) sprintf(line, "\nSubtopics available for %s:\n", 
  630.                         key->key);
  631.                 else
  632.                   (void) sprintf(line, "\nHelp topics available:\n");
  633.                 OutLine(line);
  634.                 *line = '\0';
  635.                 pos = 0;
  636.              }
  637.              if (pos == PER_LINE) {
  638.                 (void) strcat(line, "\n");
  639.                 OutLine(line);
  640.                 *line = '\0';
  641.                 pos = 0;
  642.              }
  643.              /* adapted by DvdSchaaf */
  644.              {
  645. #define FIRSTCOL    6
  646. #define COLLENGTH    15
  647.                 int spacelen, ispacelen;
  648.  
  649.                  if( pos == 0 )
  650.                     spacelen = FIRSTCOL;
  651.                  for( ispacelen = 0;
  652.                     ispacelen < spacelen; ispacelen++ )
  653.                     (void) strcat(line, " ");
  654.                  /* commented out *
  655.                  (void) strcat(line, "\t");
  656.                  */
  657.                  (void) strncat(line, start, sublen);
  658.                  spacelen = COLLENGTH - sublen;
  659.                  if( spacelen <= 0 )
  660.                     spacelen = 1;
  661.              }
  662.              pos++;
  663.              prev = start;
  664.           }
  665.        } else {
  666.           /* new topic */
  667.           break;
  668.        }
  669.     }
  670.     
  671.     /* put out the last line */
  672.     if (subt > 0 && pos > 0) {
  673.        (void) strcat(line, "\n");
  674.        OutLine(line);
  675.     }
  676.     
  677. /*
  678.     if (subt == 0) {
  679.        OutLine("\n");
  680.        OutLine("No subtopics available\n");
  681.     }
  682. */
  683.     
  684.     if (subtopics)
  685.      *subtopics = (subt != 0);
  686. }
  687.  
  688.  
  689. /* StartOutput:
  690.  * Open a file pointer to a pipe to user's $PAGER, if there is one,
  691.  * otherwise use our own pager.
  692.  */
  693. static void
  694. StartOutput()
  695. {
  696. #ifdef unix
  697.     char *pager_name = getenv("PAGER");
  698.     extern FILE *popen();
  699.  
  700.     if (pager_name != NULL && *pager_name != '\0')
  701.      if ((outfile = popen(pager_name, "w")) != (FILE *)NULL)
  702.        return;            /* success */
  703.     outfile = stderr;
  704.     /* fall through to built-in pager */
  705. #endif
  706.  
  707.     /* built-in pager */
  708.     pagelines = 0;
  709. }
  710.  
  711. /* write a line of help output  */
  712. /* line should contain only one \n, at the end */
  713. static void
  714. OutLine(line)
  715.     char *line;
  716. {
  717.     int c;                /* dummy input char */
  718. #ifdef unix
  719.     if (outfile != stderr) {
  720.        fputs(line, outfile);
  721.        return;
  722.     }
  723. #endif
  724.  
  725.     /* built-in dumb pager */
  726.     /* leave room for prompt line */
  727.     if (pagelines >= SCREENSIZE - 2) {
  728.        fprintf(stderr,"Press return for more: ");
  729.        do 
  730.         c = getchar();
  731.        while (c != EOF && c != '\n');
  732.        pagelines = 0;
  733.     }
  734.     fputs(line, stderr);
  735.     pagelines++;
  736. }
  737.  
  738. static void
  739. EndOutput()
  740. {
  741. #ifdef unix
  742.     extern int pclose();
  743.  
  744.     if (outfile != stderr)
  745.      (void) pclose(outfile);
  746. #endif
  747. }
  748.